package com.jeasonzhao.commons.json;

import com.jeasonzhao.commons.basic.StringCollection;
import com.jeasonzhao.commons.parser.lex.LexException;
import com.jeasonzhao.commons.parser.lex.LexTokenCollection;
import com.jeasonzhao.commons.utils.DataTypes;

public class JSONClass extends JSONNode
{
    private static final long serialVersionUID = 1L;
    private java.util.Hashtable<String,JSONAssign> m_hashAssigns = null;
    public JSONClass()
    {
        super(JSONNodeTypes.CLASS);
    }

    public JSONClass(Object ...parameters)
    {
        this();
        if(parameters == null || parameters.length < 1)
        {
            return;
        }
        else if(parameters.length == 1)
        {
            if(null == parameters[0] || DataTypes.isPrimitive(parameters[0].getClass()))
            {
                return;
            }
            else
            {
                JSONNode node = JSONNode.object2JSONNodeSafe(parameters[0]);
                if(node.getNodeType() == JSONNodeTypes.CLASS)
                {
                    JSONClass c = (JSONClass) node;
                    this.m_hashAssigns = c.m_hashAssigns;
                    this.javaScriptCompatible = c.javaScriptCompatible;
                    this.m_tokens = c.m_tokens;
                }
            }
        }
        else
        {
            this.set(parameters);
        }
    }

    private boolean removeEmptyString = true;
    public JSONClass(boolean doesRemoveEmptyString)
    {
        this();
        removeEmptyString = doesRemoveEmptyString;
    }

    public static JSONClass from(String strJSON)
        throws JSONException
    {
        try
        {
            JSONParser parser = new JSONParser(strJSON);
            JSONNode node = parser.next();
            if(null == node)
            {
                return null;
            }
            else if(node.getNodeType() != JSONNodeTypes.CLASS)
            {
                throw new JSONException(node,"Error: can not convert a " + node.getNodeType() + " to " + JSONNodeTypes.CLASS);
            }
            else
            {
                return(JSONClass) node;
            }
        }
        catch(LexException ex)
        {
            throw new JSONException((JSONNode)null,"Parse Error:" + ex.getMessage());
        }
    }

    public JSONClass(LexTokenCollection tks)
        throws JSONException,LexException
    {
        super(JSONNodeTypes.CLASS);
        setTokens(tks);
    }

    private void setTokens(LexTokenCollection tks)
        throws LexException,JSONException
    {
        this.m_tokens = tks;
        JSONParser p = new JSONParser(m_tokens.slice(1,m_tokens.size() - 1));
        JSONNode node = p.next();
        if(null == node)
        {
            return;
        }
        if(node.m_nNodeType != JSONNodeTypes.ASSIGN)
        {
            throw new JSONException(this,node,"JSON Error: can not use non-assignment in a JSON string");
        }
        else
        {
            this.setSingle(((JSONAssign) node).getName(),node);
        }
        while(true)
        {
            node = p.next();
            if(node == null)
            {
                break;
            }
            if(node.m_nNodeType != JSONNodeTypes.SPECIAL)
            {
                throw new JSONException(this,node,"JSON Error: Incorrect dilimiter:" + node.toString());
            }
            JSONSpecial specNode = (JSONSpecial) node;
            if(specNode.getValue() == null || specNode.getValue().equals(JSONParser.SPEC_ARRAY_DELIMETER) == false)
            {
                throw new JSONException(this,node,"JSON Error: Incorrect dilimiter:" + node.toString());
            }
            node = p.next();
            if(node == null)
            {
                break;
            }
            if(node.m_nNodeType != JSONNodeTypes.ASSIGN)
            {
                throw new JSONException(this,node,"JSON Error: can not use non-assignment in a JSON string");
            }
            else
            {
                this.setSingle(((JSONAssign) node).getName(),node);
            }
        }
    }

    public int size()
    {
        return null == m_hashAssigns ? 0 : m_hashAssigns.size();
    }

    public String toJSON(boolean isFullMode,int nLevel)
    {
        StringBuffer buf = new StringBuffer();
        buf.append(JSONParser.CLASS_BEGIN);
        if(this.size() > 0)
        {
            boolean contains = false;
            for(JSONNode subNode : this.m_hashAssigns.values())
            {
                String nodeString = subNode
                    .setJavaScriptCompatible(this.javaScriptCompatible)
                    .toJSON(isFullMode,nLevel + 1).trim();
                if(false == isFullMode && (nodeString == null || nodeString.length() < 1))
                {
                    continue;
                }
                if(isFullMode)
                {
                    buf.append("\r\n" + this.getPrefixSpace(nLevel));
                }
                if(contains)
                {
                    buf.append(JSONParser.SPEC_CLASS_DELIMETER);
                }
                contains = true;
                buf.append(nodeString);
            }
        }
        if(isFullMode)
        {
            buf.append("\r\n" + this.getPrefixSpace(nLevel - 1));
        }
        buf.append(JSONParser.CLASS_END);
        return buf.toString();

    }

    public JSONClass set(Object ...parameters)
    {
        if(null == parameters)
        {
            return this;
        }
        int nindex = 0;
        while(nindex < parameters.length)
        {
            Object obj = parameters[nindex];
            nindex++;
            if(null == obj)
            {
                continue;
            }
            if(obj instanceof JSONAssign)
            {
                this.setSingle(((JSONAssign) obj).getName(),obj);
                continue;
            }
            else if(obj instanceof JSONNode)
            {
                continue;
            }
            else
            {
                String key = obj.toString();
                Object value = nindex < parameters.length ? parameters[nindex] : null;
                if(false == (removeEmptyString && (value == null || value.toString().trim().length() < 1)))
                {
                    this.setSingle(key,value);
                }
                nindex++;
            }
        }
        return this;
    }

    private JSONClass setSingle(String strName,Object objValue)
    {
        if(strName == null || strName.trim().length() < 1)
        {
            return this;
        }
        strName = strName.trim();
        if(null == objValue)
        {
            objValue = "";
        }
        JSONAssign assign = null;
        if(objValue instanceof JSONAssign)
        {
            assign = (JSONAssign) objValue;
        }
        else
        {
            assign = objValue instanceof JSONNode
                ? new JSONAssign(strName,(JSONNode) objValue)
                : new JSONAssign(strName,JSONNode.object2JSONNodeSafe(objValue));
        }
        //System.out.println(assign.getName()+"="+assign.getValue());
        if(null == m_hashAssigns)
        {
            m_hashAssigns = new java.util.Hashtable<String,JSONAssign>();
        }
        m_hashAssigns.put(strName,assign);
        return this;
    }

    public String valueOf(String ...attributesNames)
    {
        if(null == attributesNames || this.size() < 1)
        {
            return null;
        }
        for(String str : attributesNames)
        {
            JSONNode node = getNode(str);
            if(null == node || node.m_nNodeType != JSONNodeTypes.CONSTANT)
            {
                continue;
            }
            else
            {
                return((JSONConstant) node).getValue();
            }
        }
        return null;
    }

    public JSONNode getNode(String ...parameters)
    {
        if(null == parameters || this.size() < 1)
        {
            return null;
        }
        for(String str : parameters)
        {
            for(JSONAssign as : this.m_hashAssigns.values())
            {
                if(str.equalsIgnoreCase(as.getName()))
                {
                    return as.getValue();
                }
            }
        }
        return null;
    }

    public JSONArray createSubArray(String name)
    {
        if(name == null || name.trim().length() < 1)
        {
            return null;
        }
        JSONArray ary = new JSONArray();
        this.setSingle(name,ary);
        return ary;
    }

    public JSONClass createSubClass(String name)
    {
        if(name == null || name.trim().length() < 1)
        {
            return null;
        }
        JSONClass ary = new JSONClass();
        this.setSingle(name,ary);
        return ary;
    }

    public JSONClass removeNode(String ...strNames)
    {
        if(null != strNames)
        {
            for(String strName : strNames)
            {
                if(null != strName)
                {
                    this.m_hashAssigns.remove(strName);
                }
            }
        }
        return this;
    }
    public boolean containsName(String... name)
    {
        return this.getNode(name)!=null;
    }
    public JSONArray arrayNode(String strName)
    {
        return(JSONArray)this.getNode(strName);
    }

    public StringCollection getVariableNames()
    {
        return new StringCollection(this.m_hashAssigns.keySet());
    }
    public static final JSONClass create(){
        return new JSONClass();
    }
}
